package utils;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;

import org.apache.commons.lang3.StringUtils;
//import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.HibernateValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import com.google.common.collect.Maps;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil;
import io.swagger.annotations.ApiModelProperty;

/**
 * 校验工具类
 *
 * @author jiangfeng <jiangfeng@kuaishou.com>
 * Created on 2019-10-22
 */
public class ValidUtils<T> {

    public static Logger log = LoggerFactory.getLogger(ValidUtils.class);

    // HibernateValidator 来源: org.hibernate.validator:hibernate-validator:6.2.5.Final
    private static Validator validator = Validation.byProvider(HibernateValidator.class)
            .configure()
            .failFast(false)// 不等校验完发现一个既返回错误
            .buildValidatorFactory()
            .getValidator();
    ;

    /**
     * map 不覆盖写
     */
    private static Map<Object, String> noCoverPut(Map map, Object key, String value) {
        if (map.containsKey(key)) {
            map.put(key, map.get(key) + ", " + value);
        } else {
            map.put(key, value);
        }
        return map;
    }

    public static String getFieldDesc(Object o, String field) {
        return getFieldDesc(o.getClass(), field);
    }

    public static String getFieldDesc(Class clazz, String field) {
        return getFieldDesc(ReflectUtil.getField(clazz, field));
    }

    public static String getFieldDesc(Field field) {
        if (field == null) {
            return "";
        }
        ApiModelProperty apiModelProperty = AnnotationUtil.getAnnotation(field, ApiModelProperty.class);
        if (apiModelProperty != null) {
            return apiModelProperty.value();
        }
        return "";
    }


    public static String mapToStr(Map<String, String> errors) {
        //return errors.entrySet().stream().map(e -> e.getKey() + " " + e.getValue()).collect(Collectors.joining(","));
        return errors.entrySet().stream().map(e -> e.getValue()).collect(Collectors.joining(","));
    }

    public static <T> void validateObj(T t, Class<?>... groups) {
        Map<String, String> errors = validateObjReturnError(t, groups);
        if (!CollectionUtils.isEmpty(errors)) {
            throw new ValidationException(mapToStr(errors));
        }
    }

    /**
     * 校验指定字段
     */
    public static <T> void validateObj(T t, List<String> properties, Class<?>... groups) {
        Map<String, String> errors = validateObjReturnError(t, properties, groups);
        if (!CollectionUtils.isEmpty(errors)) {
            throw new ValidationException(errors.values().toString() + t);
        }
    }

    public static <T> void validateList(List<T> list, Class<?>... groups) {
        Map<String, String> errors = validateListReturnError(list, groups);
        if (!CollectionUtils.isEmpty(errors)) {
            throw new ValidationException(errors.keySet().toString() + errors.values().toString() + list);
        }
    }

    public static <T> Map<String, String> validateObjReturnError(T t, List<String> properties, Class<?>... groups) {
        LinkedHashMap<String, String> errors = Maps.newLinkedHashMap();
        if (t == null) {
            return errors;
        }
        Set<ConstraintViolation<T>> validateResult = new HashSet<>();
        for (String property : properties) {
            if (StringUtils.isBlank(property)) {
                continue;
            }
            Set<ConstraintViolation<T>> constraintViolations1 = validator.validateProperty(t, property, groups);
            //Set<? extends ConstraintViolation<?>> constraintViolations =
            //      validator.validateValue(t.getClass(), property, ReflectUtil.getFieldValue(t, property), groups);
            validateResult.addAll(constraintViolations1);
        }
        if (!validateResult.isEmpty()) {
            validateResult.forEach(s -> {
                String field = s.getPropertyPath().toString();
                log.info("字段校验出错! 字段={},message={}", field, s.getMessage());
                noCoverPut(errors, getFieldDesc(t, field), s.getMessage());
            });
        }
        return errors;
    }

    public static <T> Map<String, String> validateListReturnError(List<T> list, Class<?>... groups) {
        LinkedHashMap<String, String> errors = Maps.newLinkedHashMap();
        if (CollectionUtils.isEmpty(list)) {
            return errors;
        }
        list.forEach(l -> {
            if (l != null) {
                Set<ConstraintViolation<T>> validateResult = validator.validate(l, groups);
                if (!CollectionUtils.isEmpty(validateResult)) {
                    validateResult.forEach(s -> {
                        String field = s.getPropertyPath().toString();
                        //field = getFieldDesc(s.getRootBean(), field);
                        log.info("字段校验出错! 字段={},message={}", field, s.getMessage());
                        noCoverPut(errors, field, s.getMessage());
                    });
                }
            }
        });
        return errors;
    }

    public static <T> Map<String, String> validateObjReturnError(T t, Class<?>... groups) {
        LinkedHashMap<String, String> errors = Maps.newLinkedHashMap();
        if (t == null) {
            return errors;
        }
        Set<ConstraintViolation<T>> validateResult = validator.validate(t, groups);

        if (!validateResult.isEmpty()) {
            for (ConstraintViolation<T> s : validateResult) {
                String field = s.getPropertyPath().toString();
                // field = getFieldDesc(s.getRootBean(), field);
                log.info("字段校验出错! 字段={},message={}", field, s.getMessage());
                noCoverPut(errors, field, s.getMessage());
            }
        }
        return errors;
    }


}
